/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.common.base.service;

import com.je.common.base.DynaBean;
import com.je.common.base.log.CheckSlowSql;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.ibatis.extension.enums.DbType;
import com.je.ibatis.extension.plugins.pagination.Page;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * MetaService
 *
 * @author wangmm@ketr.com.cn
 * @date 2019/12/4
 */
public interface MetaService {

    /**
     * 获取数据库连接对象
     *
     * @return Connection 数据库连接
     */
    Connection getConnection();

    /**
     * 获取数据库类型
     *
     * @return DbType 数据库类型
     */
    DbType getDbType();

    /**
     * 清空缓存
     */
    void clearMyBatisCache();

    /**
     * 清空缓存
     *
     * @param tableCode 表名
     */
    void clearMyBatisTableCache(String tableCode);

    /**
     * 清空缓存
     */
    void clearMyBatisFuncCache();

    /**
     * 清空缓存
     *
     * @param funcCode 功能编码
     */
    void clearMyBatisFuncCache(String funcCode);

    /**
     * 插入
     *
     * @param beanMap 字段值
     * @return int 影响记录数
     */
    default int insert(DynaBean beanMap) {
        return insert(beanMap.getTableCode(), beanMap);
    }

    /**
     * 插入
     *
     * @param tableCode 表名
     * @param beanMap   字段值
     * @return int 影响记录数
     */
    int insert(String tableCode, DynaBean beanMap);

    /**
     * 插入并返回bean信息
     *
     * @param beanMap 字段值
     * @return 插入的bean
     */
    DynaBean insertAndReturn(DynaBean beanMap);

    /**
     * 批量插入
     *
     * @param list 被插入数据list
     * @return int 影响记录数
     */
    default int insertBatch(List<DynaBean> list) {
        return insertBatch(null, list);
    }

    /**
     * 批量插入
     *
     * @param tableCode 表名
     * @param list      被插入数据list
     * @return int 影响记录数
     */
    int insertBatch(String tableCode, List<DynaBean> list);

    /**
     * 修改
     *
     * @param beanMap 字段值
     * @return int 影响记录数
     */
    default int update(DynaBean beanMap) {
        return update(beanMap.getTableCode(), beanMap.getPkValue(), beanMap, null);
    }

    /**
     * 修改
     *
     * @param beanMap 字段值
     * @param wrapper where条件
     * @return int 影响记录数
     */
    default int update(DynaBean beanMap, ConditionsWrapper wrapper) {
        return update(beanMap.getTableCode(), beanMap.getPkValue(), beanMap, wrapper);
    }

    /**
     * 修改
     *
     * @param tableCode 表名
     * @param pkValue   主键
     * @param beanMap   字段值
     * @param wrapper   where条件
     * @return int 影响记录数
     */
    int update(String tableCode, String pkValue, DynaBean beanMap, ConditionsWrapper wrapper);

    /**
     * 修改并返回bean信息
     *
     * @param beanMap
     * @return 修改完后的bean
     */
    DynaBean updateAndReturn(DynaBean beanMap);

    /**
     * 删除
     * <p>
     * 使用条件解析器的表名
     *
     * @param wrapper 条件
     * @return int 影响记录数
     */
    default int delete(ConditionsWrapper wrapper) {
        return delete(null, wrapper);
    }

    /**
     * 删除
     *
     * @param tableCode 表名
     * @param wrapper   条件
     * @return int 影响记录数
     */
    int delete(String tableCode, ConditionsWrapper wrapper);

    /**
     * 查询数据使用自定义sql
     * <p>
     * 不分页
     *
     * @param sql    原始sql
     * @param params sql参数数组
     * @return List
     */
    @CheckSlowSql
    default List<Map<String, Object>> selectSql(String sql, Object... params) {
        return selectSql(-1, -1, sql, params);
    }

    /**
     * 查询数据使用自定义sql
     * <p>
     * 不分页
     *
     * @param current 第几页
     * @param size    每页数量
     * @param sql     自定义sql
     * @param params  sql参数数组
     * @return List
     */
    @CheckSlowSql
    default List<Map<String, Object>> selectSql(int current, int size, String sql, Object... params) {
        return selectSql(new Page<>(current, size), ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 查询数据使用自定义sql
     * <p>
     * 不分页
     *
     * @param wrapper 查询sql
     * @return List
     */
    @CheckSlowSql
    default List<Map<String, Object>> selectSql(ConditionsWrapper wrapper) {
        return selectSql(null, wrapper);
    }

    /**
     * 查询数据使用自定义sql
     *
     * @param page    分页对象
     * @param wrapper 查询sql
     * @return List
     */
    List<Map<String, Object>> selectSql(Page page, ConditionsWrapper wrapper);

    /**
     * 查询数据使用自定义sql
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @return
     */
    List<DynaBean> selectBeanSql(String tableCode, String sql);

    /**
     * 查询数据使用自定义sql
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @param params    查询参数
     * @return
     */
    List<DynaBean> selectBeanSql(String tableCode, String sql, Object... params);

    /**
     * 使用自定义sql查询一条数据
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @return
     */
    DynaBean selectOneBeanSql(String tableCode, String sql);

    /**
     * 使用sql查询一条数据
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @param params    自定义参数
     * @return
     */
    DynaBean selectOneBeanSql(String tableCode, String sql, Object... params);


    /**
     * 查询数据使用自定义sql
     *
     * @param tableCode 表名
     * @param current   第几页
     * @param size      每页数量
     * @param sql       where条件
     * @param params    sql参数数组
     * @return List
     */
    default List<DynaBean> select(String tableCode, int current, int size, String sql, Object... params) {
        return select(tableCode, current, size, ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 查询数据使用自定义sql
     *
     * @param tableCode 表名
     * @param page      分页对象
     * @param sql       where条件
     * @param params    sql参数数组
     * @return List
     */
    default List<DynaBean> select(String tableCode, Page page, String sql, Object... params) {
        return select(tableCode, page, ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 查询数据
     * <p>
     * 不分页，使用条件解析器的表名
     *
     * @param wrapper 条件
     * @return List
     */
    @CheckSlowSql
    default List<DynaBean> select(ConditionsWrapper wrapper) {
        return select(null, -1, -1, wrapper);
    }

    /**
     * 查询数据
     * <p>
     * 不分页
     *
     * @param tableCode 表名
     * @param wrapper   条件
     * @return List
     */
    @CheckSlowSql
    default List<DynaBean> select(String tableCode, ConditionsWrapper wrapper) {
        return select(tableCode, -1, -1, wrapper);
    }

    /**
     * 查询数据
     * <p>
     * 不分页
     *
     * @param tableCode 表名
     * @param wrapper   条件
     * @return List
     */
    @CheckSlowSql
    default List<DynaBean> select(String tableCode, ConditionsWrapper wrapper, String columns) {
        return select(tableCode, new Page(-1, -1), wrapper, columns);
    }

    /**
     * 查询数据
     * <p>
     * 默认读取第一页
     *
     * @param tableCode 表名
     * @param size      每页数量
     * @param wrapper   条件
     * @return List
     */
    default List<DynaBean> select(String tableCode, int size, ConditionsWrapper wrapper) {
        return select(tableCode, 1, size, wrapper);
    }

    /**
     * 查询数据
     * <p>
     * 使用条件解析器的表名
     *
     * @param current 第几页
     * @param size    每页数量
     * @param wrapper 条件
     * @return List
     */
    default List<DynaBean> select(int current, int size, ConditionsWrapper wrapper) {
        return select(null, current, size, wrapper);
    }

    /**
     * 查询数据
     * <p>
     * 不使用条件解析器
     *
     * @param tableCode 表名
     * @param current   第几页
     * @param size      每页数量
     * @return List
     */
    default List<DynaBean> select(String tableCode, int current, int size) {
        return select(tableCode, current, size, null);
    }

    /**
     * 查询数据
     *
     * @param tableCode 表名
     * @param current   第几页
     * @param size      每页数量
     * @param wrapper   条件
     * @return List
     */
    @CheckSlowSql
    default List<DynaBean> select(String tableCode, int current, int size, ConditionsWrapper wrapper) {
        return select(tableCode, new Page(current, size), wrapper);
    }

    /**
     * 查询数据
     *
     * @param tableCode 表名
     * @param page      分页对象
     * @param wrapper   条件
     * @return List
     */
    @CheckSlowSql
    default List<DynaBean> select(String tableCode, Page page, ConditionsWrapper wrapper) {
        return select(tableCode, page, wrapper, null);
    }

    /**
     * 查询数据
     *
     * @param tableCode 表名
     * @param page      分页对象
     * @param wrapper   条件
     * @param columns   要查询的列
     * @return List
     */
    List<DynaBean> select(String tableCode, Page page, ConditionsWrapper wrapper, String columns);

    /**
     * 查询一条数据
     * <p>
     * 查询结果数量大于1 抛出异常
     *
     * @param tableCode 表名
     * @param wrapper   条件
     * @return List
     */
    @CheckSlowSql
    default DynaBean selectOne(String tableCode, ConditionsWrapper wrapper) {
        return selectOne(tableCode, wrapper, null);
    }

    /**
     * 查询一条数据
     * <p>
     * 查询结果数量大于1 抛出异常
     *
     * @param tableCode 表名
     * @param wrapper   条件
     * @param columns   要查询的列
     * @return List
     */
    DynaBean selectOne(String tableCode, ConditionsWrapper wrapper, String columns);

    /**
     * 加载功能数据
     *
     * @param funcCode 功能code
     * @param page     分页对象
     * @param sql      原始sql
     * @param params   sql参数数组
     * @return List
     */
    @CheckSlowSql
    default List<Map<String, Object>> load(String funcCode, Page page, String sql, Object... params) {
        return load(funcCode, page, ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 加载功能数据
     *
     * @param funcCode 功能code
     * @param page     分页对象
     * @param wrapper  条件
     * @return List
     */
    List<Map<String, Object>> load(String funcCode, Page page, ConditionsWrapper wrapper);

    /**
     * 根据主键查询数据
     *
     * @param tableCode 表名
     * @param pkValue   主键值
     * @return com.je.core.util.bean.DynaBean
     */
    default DynaBean selectOneByPk(String tableCode, String pkValue) {
        return selectOneByPk(tableCode, pkValue, null);
    }

    /**
     * 根据主键查询数据
     *
     * @param tableCode 表名
     * @param pkValue   主键值
     * @param columns   要查询的列
     * @return com.je.core.util.bean.DynaBean
     */
    DynaBean selectOneByPk(String tableCode, String pkValue, String columns);

    /**
     * 执行sql
     * <p>
     * in类型预处理参数，禁用数组！禁用数组！禁用数组！统一使用Collection子类，如 List Set
     *
     * @param sql    自定义sql
     * @param params sql参数数组
     * @return int 影响记录数
     */
    default int executeSql(String sql, Object... params) {
        return executeSql(ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 执行总数查询sql
     *
     * @param wrapper 查询sql
     * @return long count值
     */
    int executeSql(ConditionsWrapper wrapper);

    /**
     * 执行总数查询sql
     *
     * @param sql    自定义sql
     * @param params sql参数数组
     * @return long count值
     */
    @CheckSlowSql
    default long countBySql(String sql, Object... params) {
        return countBySql(ConditionsWrapper.builder().apply(sql, params));
    }

    /**
     * 执行总数查询sql
     *
     * @param wrapper 查询sql
     * @return long count值
     */
    long countBySql(ConditionsWrapper wrapper);

    /**
     * 查询数量
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @return
     */
    @CheckSlowSql
    default long selectCount(String tableCode, String sql) {
        return countBySql(ConditionsWrapper.builder().table(tableCode).apply(sql, null));
    }

    /**
     * 查询数量
     *
     * @param tableCode 表名
     * @param sql       查询sql
     * @param params    查询参数
     * @return
     */
    @CheckSlowSql
    default long selectCount(String tableCode, String sql, Object... params) {
        return countBySql(ConditionsWrapper.builder().table(tableCode).apply(sql, params));
    }

    /**
     * 执行总数查询sql
     *
     * @param wrapper iBatis查询构造器
     * @return HibernateWrapper Hibernate Sql预处理封装对象
     */
    default HibernateWrapper buildHibernateWrapper(ConditionsWrapper wrapper) {
        return new HibernateWrapper(wrapper);
    }

    class HibernateWrapper {

        private String sql;
        private Map<String, Object> params;

        public HibernateWrapper(ConditionsWrapper wrapper) {
            this.params = wrapper.getParameter();
            this.sql = formatNameParamsSql(wrapper.getSql());
        }

        /**
         * 匹配 #{wrapper.parameter.xxxxx}
         */
        private static final Pattern PATTERN = Pattern.compile("(#\\{wrapper\\.parameter\\.(.*?)})", Pattern.MULTILINE);

        private String formatNameParamsSql(String iBatisSql) {

            Matcher matcher = PATTERN.matcher(iBatisSql);
            while (matcher.find()) {
                String p = matcher.group();
                //获取参数名 #{wrapper.parameter.xxxxx} ==> xxxxx
                String parameter = p.substring(20, p.length() - 1);
                //hibernate预处理参数格式 :xxxxx
                String key = ":" + parameter;
                //替换变量为key
                iBatisSql = iBatisSql.replace(p, key);
            }
            return iBatisSql;
        }

        public String getSql() {
            return sql;
        }

        public Map<String, Object> getParams() {
            return params;
        }
    }
}
